package main

import (
	"flag"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
	"golang.org/x/sync/errgroup"
	"gopkg.in/yaml.v3"
	"io/ioutil"
	"log"
	"net/http"
	"sensi/cmd/api/routers"
	"sensi/common/frame"
	"sensi/common/sys"
	"sensi/common/sys/database"
	"sensi/common/sys/logger"
	"time"
)

var configPath = flag.String("c", "config/api.yaml", "config file path")

// Server ...
type Server struct {
	Env              string `yaml:"env"`
	Address          string `yaml:"address"`
	ReadTimeout      int    `yaml:"read_timeout"`
	WriteTimeout     int    `yaml:"write_timeout"`
	IsSSL            bool   `yaml:"is_ssl"`
	SSLAddress       string `yaml:"ssl_address"`
	SSLCrt           string `yaml:"ssl_crt"`
	SSLKey           string `yaml:"ssl_key"`
	CORSOrigin       string `yaml:"cors_origin"`
	UploadLinkPrefix string `yaml:"upload_link_prefix"`
}

// DefaultServer ...
func DefaultServer() *Server {
	return &Server{
		Env:          "dev",
		Address:      ":80",
		ReadTimeout:  60,
		WriteTimeout: 60,
	}
}

type config struct {
	Server   *Server          `yaml:"server"`
	Logger   *logger.Config   `yaml:"logger"`
	Database *database.Config `yaml:"database"`
}

func defaultConfig() *config {
	return &config{
		Server:   DefaultServer(),
		Logger:   logger.DefaultConfig(),
		Database: database.DefaultConfig(),
	}
}

func main() {
	flag.Parse()
	cfg := defaultConfig()
	str, err := ioutil.ReadFile(*configPath)
	if err != nil {
		log.Fatalf("read config file failure: %s", err)
	}
	if err := yaml.Unmarshal(str, cfg); err != nil {
		log.Fatalf("config unmarshal failure: %s", err)
	}

	if err := run(cfg); err != nil {
		log.Fatalf("run server failure: %s", err)
	}
}

func run(c *config) error {
	fmt.Println(c.Server)
	sys.Env = c.Server.Env
	sys.CORSOrigin = c.Server.CORSOrigin
	sys.UploadLinkPrefix = c.Server.UploadLinkPrefix

	if err := logger.Setup(c.Logger); err != nil {
		return errors.Wrap(err, "setup logger failure")
	}
	if err := database.Setup(c.Database); err != nil {
		return errors.Wrap(err, "setup database failure")
	}

	runMode := ""
	switch c.Server.Env {
	case frame.EnvDevelopment:
		runMode = gin.DebugMode
	case frame.EnvTest:
		runMode = gin.TestMode
	case frame.EnvPrepare, frame.EnvProduct:
		runMode = gin.ReleaseMode
	default:
		runMode = gin.DebugMode
	}
	gin.SetMode(runMode)

	var g errgroup.Group
	serverHttp := &http.Server{
		Addr:         c.Server.Address,
		Handler:      routers.InitRouter(),
		ReadTimeout:  time.Duration(c.Server.ReadTimeout) * time.Second,
		WriteTimeout: time.Duration(c.Server.WriteTimeout) * time.Second,
	}
	g.Go(func() error {
		return errors.Wrap(serverHttp.ListenAndServe(), "run http server failure")
	})

	if c.Server.IsSSL {
		serverHttps := &http.Server{
			Addr:         c.Server.SSLAddress,
			Handler:      routers.InitRouter(),
			ReadTimeout:  time.Duration(c.Server.ReadTimeout) * time.Second,
			WriteTimeout: time.Duration(c.Server.WriteTimeout) * time.Second,
		}
		g.Go(func() error {
			return errors.Wrap(serverHttps.ListenAndServeTLS(c.Server.SSLCrt, c.Server.SSLKey),
				"run tls http server failure")
		})
	}
	if err := g.Wait(); err != nil {
		return err
	}

	return nil
}
